{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7765UFHoyGx6"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "KVtTDrUNyL7x"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xPYxZMrWyA0N"
      },
      "source": [
        "# Estimator를 사용하는 부스트 트리"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p_vOREjRx-Y0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"><a>TensorFlow.org에서 보기</a>\n",
        "</td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/estimator/boosted_trees.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행하기</a></td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\"><a>GitHub에서 소스 보기</a>\n",
        "</td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/boosted_trees.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dW3r7qVxzqN5"
      },
      "source": [
        "이 튜토리얼은 `tf.estimator` API와 함께 의사 결정 트리를 사용하여 그래디언트 부스팅 모델을 훈련하기 위한 전체 연습입니다. 부스트 트리 모델은 회귀 및 분류에 가장 널리 사용되고 효과적인 머신러닝 방식 중 하나입니다. 여러(수십, 수백 또는 수천) 트리 모델의 예측을 결합한 조화로운 기법입니다.\n",
        "\n",
        "부스트 트리 모델은 최소한의 하이퍼 매개변수 튜닝으로 인상적인 성능을 얻을 수 있기 때문에 많은 머신러닝 전문가 사이에서 인기가 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eylrTPAN3rJV"
      },
      "source": [
        "## titanic 데이터세트 로드하기\n",
        "\n",
        "여기서는 titanic 데이터세트를 이용하며 (다소 음산한) 목표는 성별, 나이, 등급 등과 같은 특징을 고려하여 승객 생존을 예측하는 것입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KuhAiPfZ3rJW"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "from IPython.display import clear_output\n",
        "from matplotlib import pyplot as plt\n",
        "\n",
        "# Load dataset.\n",
        "dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')\n",
        "dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')\n",
        "y_train = dftrain.pop('survived')\n",
        "y_eval = dfeval.pop('survived')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NFtnFm1T0kMf"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "tf.random.set_seed(123)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3ioodHdVJVdA"
      },
      "source": [
        "데이터세트는 훈련 세트와 평가 세트로 구성됩니다.\n",
        "\n",
        "- `dftrain` 및 `y_train`은 *훈련 세트*입니다(모델이 학습에 사용하는 데이터).\n",
        "- 이 모델은 *eval set* , `dfeval` 및 `y_eval`을 대상으로 테스트됩니다.\n",
        "\n",
        "훈련을 위해 다음 특성을 사용합니다.\n",
        "\n",
        "<table>\n",
        "  <tr>\n",
        "    <th>특성 이름</th>\n",
        "    <th>설명</th>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>sex</td>\n",
        "    <td>승객의 성별</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>age</td>\n",
        "    <td>승객의 나이</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>n_siblings_spouses</td>\n",
        "    <td>형제 자매 및 파트너 탑승</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>parch</td>\n",
        "    <td>부모 및 자녀 탑승</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>fare</td>\n",
        "    <td>승객이 지불한 요금</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>수업</td>\n",
        "    <td>유람선의 승객 등급</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>deck</td>\n",
        "    <td>승객이 있었던 갑판</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>embark_town</td>\n",
        "    <td>승객이 최초 탑승한 지역</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>alone</td>\n",
        "    <td>승객이 혼자인지 여부</td>\n",
        "  </tr>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AoPiWsJALr-k"
      },
      "source": [
        "## 데이터 탐색하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "slcat1yzmzw5"
      },
      "source": [
        "먼저 일부 데이터를 미리 살펴보고 훈련 세트에 대한 요약 통계를 작성하겠습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "15PLelXBlxEW"
      },
      "outputs": [],
      "source": [
        "dftrain.head()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j2hiM4ETmqP0"
      },
      "outputs": [],
      "source": [
        "dftrain.describe()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-IR0e8V-LyJ4"
      },
      "source": [
        "훈련 및 평가 세트에는 각각 627개와 264개의 예제가 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_1NwYqGwDjFf"
      },
      "outputs": [],
      "source": [
        "dftrain.shape[0], dfeval.shape[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "28UFJ4KSMK3V"
      },
      "source": [
        "승객의 대다수는 20대와 30대입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CaVDmZtuDfux"
      },
      "outputs": [],
      "source": [
        "dftrain.age.hist(bins=20)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1pifWiCoMbR5"
      },
      "source": [
        "탑승 성별을 보면 여성 승객보다 남성 승객이 약 2배 많습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-WazAq30MO5J"
      },
      "outputs": [],
      "source": [
        "dftrain.sex.value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7_XkxrpmmVU_"
      },
      "source": [
        "대부분의 승객은 \"3등석\"에 있었습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zZ3PvVy4l4gI"
      },
      "outputs": [],
      "source": [
        "dftrain['class'].value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HM5SlwlxmZMT"
      },
      "source": [
        "대부분의 승객은 Southampton에서 출발했습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RVTSrdr4mZaC"
      },
      "outputs": [],
      "source": [
        "dftrain['embark_town'].value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aTn1niLPob3x"
      },
      "source": [
        "남성보다 여성의 생존 가능성이 훨씬 높습니다. 이 부분은 분명히 모델의 예측 특성이 될 것입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Eh3KW5oYkaNS"
      },
      "outputs": [],
      "source": [
        "pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "krkRHuMp3rJn"
      },
      "source": [
        "## 특성 열 및 입력 함수 작성하기\n",
        "\n",
        "그래디언트 부스팅 estimator는 숫자 및 범주별 특성을 모두 사용할 수 있습니다. 특성 열은 모든 TensorFlow estimator에서 작동하며 모델링에 사용되는 특성을 정의하는 것이 목적입니다. 또한 원-핫 인코딩, 정규화 및 버킷화와 같은 몇 가지 특성 엔지니어링 기능도 제공합니다. 이 튜토리얼에서 `CATEGORICAL_COLUMNS`의 필드는 범주별 열에서 원핫-인코딩된 열([표시기 열](https://www.tensorflow.org/api_docs/python/tf/feature_column/indicator_column))로 변환됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "upaNWxcF3rJn"
      },
      "outputs": [],
      "source": [
        "CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',\n",
        "                       'embark_town', 'alone']\n",
        "NUMERIC_COLUMNS = ['age', 'fare']\n",
        "\n",
        "def one_hot_cat_column(feature_name, vocab):\n",
        "  return tf.feature_column.indicator_column(\n",
        "      tf.feature_column.categorical_column_with_vocabulary_list(feature_name,\n",
        "                                                 vocab))\n",
        "feature_columns = []\n",
        "for feature_name in CATEGORICAL_COLUMNS:\n",
        "  # Need to one-hot encode categorical features.\n",
        "  vocabulary = dftrain[feature_name].unique()\n",
        "  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))\n",
        "\n",
        "for feature_name in NUMERIC_COLUMNS:\n",
        "  feature_columns.append(tf.feature_column.numeric_column(feature_name,\n",
        "                                           dtype=tf.float32))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "74GNtFpStSAz"
      },
      "source": [
        "특성 열이 생성하는 변환을 볼 수 있습니다. 예를 들어, 단일 예에서 `indicator_column`을 사용할 때의 출력은 다음과 같습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Eaq79D9FtmF8"
      },
      "outputs": [],
      "source": [
        "example = dict(dftrain.head(1))\n",
        "class_fc = tf.feature_column.indicator_column(tf.feature_column.categorical_column_with_vocabulary_list('class', ('First', 'Second', 'Third')))\n",
        "print('Feature value: \"{}\"'.format(example['class'].iloc[0]))\n",
        "print('One-hot encoded: ', tf.keras.layers.DenseFeatures([class_fc])(example).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YbCUn3nCusC3"
      },
      "source": [
        "또한 모든 특성 열 변환도 함께 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "omIYcsVws3g0"
      },
      "outputs": [],
      "source": [
        "tf.keras.layers.DenseFeatures(feature_columns)(example).numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-UOlROp33rJo"
      },
      "source": [
        "다음으로 입력 함수를 만들어야 합니다. 이 함수는 훈련과 추론을 위해 데이터를 모델로 읽어 들이는 방법을 지정합니다. [`tf.data`](https://www.tensorflow.org/api_docs/python/tf/data) API에서 `from_tensor_slices` 메서드를 사용하여 Pandas에서 직접 데이터를 읽습니다. 이 방법은 작은 인메모리 데이터세트에 적합합니다. 큰 데이터세트의 경우 tf.data API에서 다양한 파일 형식([csv](https://www.tensorflow.org/api_docs/python/tf/data/experimental/make_csv_dataset) 포함)을 지원하므로 메모리에 맞지 않는 데이터세트를 처리할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9dquwCQB3rJp"
      },
      "outputs": [],
      "source": [
        "# Use entire batch since this is such a small dataset.\n",
        "NUM_EXAMPLES = len(y_train)\n",
        "\n",
        "def make_input_fn(X, y, n_epochs=None, shuffle=True):\n",
        "  def input_fn():\n",
        "    dataset = tf.data.Dataset.from_tensor_slices((dict(X), y))\n",
        "    if shuffle:\n",
        "      dataset = dataset.shuffle(NUM_EXAMPLES)\n",
        "    # For training, cycle thru dataset as many times as need (n_epochs=None).\n",
        "    dataset = dataset.repeat(n_epochs)\n",
        "    # In memory training doesn't use batching.\n",
        "    dataset = dataset.batch(NUM_EXAMPLES)\n",
        "    return dataset\n",
        "  return input_fn\n",
        "\n",
        "# Training and evaluation input functions.\n",
        "train_input_fn = make_input_fn(dftrain, y_train)\n",
        "eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HttfNNlN3rJr"
      },
      "source": [
        "## 모델 훈련 및 평가하기\n",
        "\n",
        "아래에서 다음 단계를 수행합니다.\n",
        "\n",
        "1. 특성과 하이퍼 매개변수를 지정하여 모델을 초기화합니다.\n",
        "2. `train_input_fn`을 사용하여 훈련 데이터를 모델에 입력하고 `train` 함수를 사용하여 모델을 훈련합니다.\n",
        "3. 평가 세트(이 예에서는 `dfeval` DataFrame)를 사용하여 모델 성능을 평가합니다. 예측이 `y_eval` 배열의 레이블과 일치하는지 확인합니다.\n",
        "\n",
        "부스트 트리 모델을 훈련시키기 전에 먼저 선형 분류자(로지스틱 회귀 모델)를 훈련시켜 보겠습니다. 보다 간단한 모델로 시작하여 벤치마크를 수립하는 것이 가장 좋습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JPOGpmmq3rJr"
      },
      "outputs": [],
      "source": [
        "linear_est = tf.estimator.LinearClassifier(feature_columns)\n",
        "\n",
        "# Train model.\n",
        "linear_est.train(train_input_fn, max_steps=100)\n",
        "\n",
        "# Evaluation.\n",
        "result = linear_est.evaluate(eval_input_fn)\n",
        "clear_output()\n",
        "print(pd.Series(result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BarkNXwA3rJu"
      },
      "source": [
        "다음으로 부스트 트리 모델을 훈련시켜 보겠습니다. 부스트 트리의 경우 회귀(`BoostedTreesRegressor`) 및 분류(`BoostedTreesClassifier`)가 지원됩니다. 등급 생존(생존 또는 비생존)을 예측하는 것이 목표이므로 `BoostedTreesClassifier`를 사용합니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tgEzMtlw3rJu"
      },
      "outputs": [],
      "source": [
        "# Since data fits into memory, use entire dataset per layer. It will be faster.\n",
        "# Above one batch is defined as the entire dataset.\n",
        "n_batches = 1\n",
        "est = tf.estimator.BoostedTreesClassifier(feature_columns,\n",
        "                                          n_batches_per_layer=n_batches)\n",
        "\n",
        "# The model will stop training once the specified number of trees is built, not\n",
        "# based on the number of steps.\n",
        "est.train(train_input_fn, max_steps=100)\n",
        "\n",
        "# Eval.\n",
        "result = est.evaluate(eval_input_fn)\n",
        "clear_output()\n",
        "print(pd.Series(result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hEflwznXvuMP"
      },
      "source": [
        "이제 훈련 모델을 사용하여 평가 세트로부터 승객을 예측할 수 있습니다. TensorFlow 모델은 한 번에 여러 예제의 배치나 모음에 대해 예측하도록 최적화되어 있습니다. 이전에는 `eval_input_fn`이 전체 평가 세트를 사용하여 정의되었습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6zmIjTr73rJ4"
      },
      "outputs": [],
      "source": [
        "pred_dicts = list(est.predict(eval_input_fn))\n",
        "probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])\n",
        "\n",
        "probs.plot(kind='hist', bins=20, title='predicted probabilities')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mBUaNN1BzJHG"
      },
      "source": [
        "마지막으로, 결과의 ROC(Receiver operating characteristic)도 볼 수 있습니다. 이로부터 참양성률과 거짓양성률 간의 상충 관계에 대한 더 나은 개념을 얻을 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NzxghvVz3rJ6"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import roc_curve\n",
        "\n",
        "fpr, tpr, _ = roc_curve(y_eval, probs)\n",
        "plt.plot(fpr, tpr)\n",
        "plt.title('ROC curve')\n",
        "plt.xlabel('false positive rate')\n",
        "plt.ylabel('true positive rate')\n",
        "plt.xlim(0,)\n",
        "plt.ylim(0,)\n",
        "plt.show()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "boosted_trees.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
